package org.dromara.tearoom.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.tearoom.api.RemoteHtlOrderService;
import org.dromara.tearoom.api.RemoteMallOrderService;
import org.dromara.tearoom.api.domain.bo.RemoteHtlOrderBo;
import org.dromara.tearoom.api.domain.vo.RemoteHtlOrderVo;
import org.dromara.tearoom.api.domain.vo.RemoteMallOrderVo;
import org.dromara.tearoom.domain.DayStat;
import org.dromara.tearoom.domain.HourStat;
import org.dromara.tearoom.domain.TerOrder;
import org.dromara.tearoom.domain.TerRoomDetails;
import org.dromara.tearoom.domain.bo.*;
import org.dromara.tearoom.domain.enums.*;
import org.dromara.tearoom.domain.vo.*;
import org.dromara.tearoom.mapper.TerOrderMapper;
import org.dromara.tearoom.mapper.TerRoomDetailsMapper;
import org.dromara.tearoom.service.*;
import org.dromara.tearoom.util.RestLogUtil;
import org.dromara.tearoom.util.TimeShiftUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.rmi.ServerException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 订单Service业务层处理
 *
 * @author xch
 * @date 2025-03-29
 */
@RequiredArgsConstructor
@EnableConfigurationProperties(WxPayProperties.class)
@Service
public class TerOrderServiceImpl implements ITerOrderService {

    private final TerOrderMapper baseMapper;
    private final TerRoomDetailsMapper roomBaseMapper;
    private final KuodaoService kuodaoService;
    private final TerLockSerialnumServiceImpl terLockSerialnumService;
    private final TerUserServiceImpl terUserService;
    private final ITerIotDeviceService terIotDeviceService;
    private final ITerCouponService couponService;
    private final ITerAccountsService accountsService;
    private final WxPayProperties wxPayProperties;
    private final PayService payService;
    private final IWxMaNoticeService wxMaNoticeService;
    private final WxPayService wxPayService;
    private static final Logger LOGGER = LoggerFactory.getLogger(RestLogUtil.class);
    @DubboReference
    private RemoteHtlOrderService remoteHtlOrderService;
    @DubboReference
    private RemoteMallOrderService remoteMallOrderService;
    /**
     * 查询订单
     *
     * @param id 主键
     * @return 订单
     */
    @Override
    public TerOrderVo queryById(String id){
        TerOrderVo vo =couponService.calculatePrice(null,LoginHelper.getAppLoginUser().getId(),id);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime dateTime = LocalDateTime.parse(vo.getStartTime(), formatter);
        vo.setStartTime(dateTime.plusMinutes(15).format(formatter));
        return vo;
    }

    /**
     * 根据微信号查询订单
     *
     * @param openIdBo
     * @return 订单
     */
    @Override
    public List<TerOrderRoomVo> queryByOpenId(TerOderOpenIdBo openIdBo){
        return baseMapper.selectOrderWithUser(openIdBo.getOpenId(),openIdBo.getOrderStatus());

    }



    /**
     * 分页查询订单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 订单分页列表
     */
    @Override
    public TableDataInfo<TerOrderVo> queryPageList(TerOrderBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TerOrder> lqw = buildQueryWrapper(bo);
        Page<TerOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 分页查询订单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 订单分页列表
     */
    @Override
    public TableDataInfo<TerOrderVo> queryAppPageList(TerOrderBo bo, PageQuery pageQuery) {
        Page<TerOrderVo> result = baseMapper.selectVoAppPage(pageQuery.build(), bo);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的订单列表
     *
     * @param bo 查询条件
     * @return 订单列表
     */
    @Override
    public List<TerOrderVo> queryList(TerOrderBo bo) {
        LambdaQueryWrapper<TerOrder> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }
    @Override
    public List<TerOrderExportVo> queryListExcel(TerOrderBo bo) {
        LambdaQueryWrapper<TerOrder> lqw = buildQueryWrapper(bo);
        List<TerOrder> list= baseMapper.selectList(lqw);
        // 使用Stream处理金额转换
        return list.stream().map(order -> {
            TerOrderExportVo vo = BeanUtils.toBean(order, TerOrderExportVo.class);
            vo.setTotalPrice(order.getTotalPrice());
            vo.setPrepayAmount(order.getPrepayAmount());
            vo.setRevenue(order.getRevenue());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public TerOrderDayVo queryOrderCount(TerUserCountTotalBo tbo) {
        if(tbo.getDaynum()!=null&&tbo.getDaynum()==0){
            return  baseMapper.selectOrderCountTd();
        }else if(tbo.getStart()!=null&&tbo.getEnd()!=null){
            return  baseMapper.selectOrderCountStarAndEnd(tbo.getStart(),tbo.getEnd());
        }else{
            return  baseMapper.selectOrderCountDn(tbo.getDaynum());
        }
    }
    @Override
    public TerOrderDayVo queryRefund(TerUserCountTotalBo tbo) {
        if(tbo.getDaynum()!=null&&tbo.getDaynum()==0){
            return  baseMapper.selectRefundAmount();
        }else if(tbo.getStart()!=null&&tbo.getEnd()!=null){
            return  baseMapper.selectRefundAmountStarAndEnd(tbo.getStart(),tbo.getEnd());
        }else{
            return  baseMapper.selectRefundAmountDn(tbo.getDaynum());
        }
    }
    @Override
    public TerOrderDayVo queryBalancePay(TerUserCountTotalBo tbo) {
        if(tbo.getDaynum()!=null&&tbo.getDaynum()==0){
            return  baseMapper.selectOrderBalancePay(tbo.getPayMethod());
        }else if(tbo.getStart()!=null&&tbo.getEnd()!=null){
            return  baseMapper.selectOrderBalancePayStarAndEnd(tbo.getStart(),tbo.getEnd(),tbo.getPayMethod());
        }else{
            return  baseMapper.selectOrderBalancePayDn(tbo.getDaynum(),tbo.getPayMethod());
        }
    }

    /**
     * 根据空间ID和时间查询是否可以预定
     *
     * @param tearoomId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public Boolean checkTearoomAvailability(Long tearoomId, String startTime, String endTime) {
        return baseMapper.checkTearoomAvailability(tearoomId,startTime,endTime)>0 ? true : false;
    }
    @Override
    public Integer payNotify(HttpServletRequest request){
        try {

            // 构建签名头信息
            String serial = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            if (StringUtils.isBlank(serial) || StringUtils.isBlank(signature) ||StringUtils.isBlank( timestamp)|| StringUtils.isBlank(nonce )) {
                return 0; // 或返回错误响应
            }
            SignatureHeader header = new SignatureHeader();
            header.setSerial(serial);
            header.setSignature(signature);
            header.setTimeStamp(timestamp);
            header.setNonce(nonce);
            // 读取回调报文
            String requestBody = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
            // 验证签名并解密报文
            WxPayOrderNotifyV3Result result = wxPayService.parseOrderNotifyV3Result(requestBody, header);
            WxPayOrderNotifyV3Result.DecryptNotifyResult decryptResult = result.getResult();
            //查询用户信息
            if(StringUtils.isBlank(decryptResult.getOutTradeNo())) {
                return 0;
            }
            LOGGER.info("微信回调通知订单：" + decryptResult.getOutTradeNo()+"金额："+decryptResult.getAmount().getTotal());

            if("TEH".equals(decryptResult.getOutTradeNo().split("-")[0])){
                TerOrder terOrder=baseMapper.selectById(decryptResult.getOutTradeNo());
                if(ObjectUtil.isEmpty(terOrder)) {
                    return 0;
                }
                LOGGER.info("回调状态:={} ", terOrder.getOrderStatus());
                if ("SUCCESS".equals(decryptResult.getTradeState())&&decryptResult.getAmount().getTotal()==terOrder.getPrepayAmount()&&OrderStatusContent.UN_PAY.equals(terOrder.getOrderStatus())) {
                    terOrder.setOrderStatus(OrderStatusContent.CONFIRMED);
                    if(StringUtils.isNotBlank(terOrder.getIsRenew())) {
                        renewOrder(terOrder);
                        return 1;
                    }
                    return baseMapper.updateById(terOrder);
                }
            }else if("HTL".equals(decryptResult.getOutTradeNo().split("-")[0])){
                RemoteHtlOrderVo htlOrder= remoteHtlOrderService.queryOrderById(decryptResult.getOutTradeNo());
                LOGGER.info("回调状态:={} ", htlOrder.getOrderStatus());
                if(ObjectUtil.isEmpty(htlOrder)) {
                    return 0;
                }
                if ("SUCCESS".equals(decryptResult.getTradeState())&&decryptResult.getAmount().getTotal()==htlOrder.getPrepayAmount()) {
                    RemoteHtlOrderBo bo = new RemoteHtlOrderBo();
                    bo.setId(decryptResult.getOutTradeNo());
                    if(htlOrder.getRenewId()!=null){
                        bo.setOrderStatus(HotelStatusContent.CHECKED_IN);
                        return remoteHtlOrderService.updateById(bo);
                    }else{
                        bo.setOrderStatus(HotelStatusContent.CONFIRMED);
                        return remoteHtlOrderService.updateById(bo);
                    }

                }
            }else if("MAL".equals(decryptResult.getOutTradeNo().split("-")[0])){
                RemoteMallOrderVo orderVo= remoteMallOrderService.queryOrderById(decryptResult.getOutTradeNo());
                LOGGER.info("回调状态:={} ", orderVo.getOrderStatus());
                if(ObjectUtil.isEmpty(orderVo)) {
                    return 0;
                }
                if ("SUCCESS".equals(decryptResult.getTradeState())&&decryptResult.getAmount().getTotal()==orderVo.getPrepayAmount()) {
                    RemoteHtlOrderBo bo = new RemoteHtlOrderBo();
                    bo.setId(decryptResult.getOutTradeNo());
                    if(orderVo.getPickupPointId()!=null) {
                        bo.setOrderStatus(MallStatusContent.SHIPMENT);
                    }else{
                        bo.setOrderStatus(MallStatusContent.CONFIRMED);
                    }
                    if(orderVo.getIsCart()==1) {
                        remoteMallOrderService.deleteByUserId(orderVo.getUserId());
                    }
                    return remoteMallOrderService.updateById(bo);
                }
            }

            return 1;
        } catch (WxPayException e) {
            return 0;
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 查询最近使用的房间订单
     *
     * @param roomId 查询条件
     * @return 订单列表
     */
    @Override
    public TerOrderVo queryRecentBookTime(Long roomId) {
        return baseMapper.selectNearestOrder(roomId);
    }

    /**
     * 统计订单数据
     *
     * @param start
     * @param end
     * @return
     */
    @Override
    public OrderStatDTO getOrderStats(LocalDate start, LocalDate end) {
        boolean isToday = start.equals(end);
        if (isToday) {
            return handleHourlyData(start);
        } else {
            return handleDailyData(start, end);
        }
    }

    private OrderStatDTO handleHourlyData(LocalDate date) {
        List<HourStat> dbData = baseMapper.getHourlyStats(date.toString());
        Map<String, Integer> dataMap = dbData.stream()
                .collect(Collectors.toMap(HourStat::getTimeUnit, HourStat::getCount));
        List<TimeUnitData> result = new ArrayList<>();
        for (int hour = 0; hour <= 24; hour++) {
            String showTimeUnit = String.format("%2d:00", hour);
            String timeUnit = String.format("%02d:00", hour);
            result.add(new TimeUnitData(
                    showTimeUnit,
                    dataMap.getOrDefault(timeUnit, 0)
            ));
        }
        return new OrderStatDTO("HOUR", result);
    }

    private OrderStatDTO handleDailyData(LocalDate start, LocalDate end) {
        List<DayStat> dbData = baseMapper.getDailyStats(start.toString(), end.toString());
        Map<String, Integer> dataMap = dbData.stream()
                .collect(Collectors.toMap(DayStat::getTimeUnit, DayStat::getCount));

        List<TimeUnitData> result = new ArrayList<>();
        LocalDate current = start;
        while (!current.isAfter(end)) {
            String dateStr = current.toString();
            result.add(new TimeUnitData(
                    dateStr,
                    dataMap.getOrDefault(dateStr, 0)
            ));
            current = current.plusDays(1);
        }
        return new OrderStatDTO("DAY", result);
    }

    private LambdaQueryWrapper<TerOrder> buildQueryWrapper(TerOrderBo bo) {
        LambdaQueryWrapper<TerOrder> lqw = Wrappers.lambdaQuery();
        lqw.orderByDesc(TerOrder::getCreateTime);
        lqw.eq(bo.getId() != null, TerOrder::getId, bo.getId());
        lqw.eq(bo.getPaymentMethod() != null, TerOrder::getPaymentMethod, bo.getPaymentMethod());
        lqw.like(StringUtils.isNotBlank(bo.getTearoomName()), TerOrder::getTearoomName, bo.getTearoomName());
        lqw.ge(bo.getStartTime() != null, TerOrder::getStartTime, bo.getStartTime());
        lqw.le(bo.getEndTime() != null, TerOrder::getEndTime, bo.getEndTime());
        lqw.eq(StringUtils.isNotBlank(bo.getOpenId()), TerOrder::getOpenId, bo.getOpenId());
        lqw.eq(StringUtils.isNotBlank(bo.getOrderStatus()), TerOrder::getOrderStatus, bo.getOrderStatus());
        lqw.in(CollUtil.isNotEmpty(bo.getOrderStatusList()), TerOrder::getOrderStatus, bo.getOrderStatusList());
        lqw.like(StringUtils.isNotBlank(bo.getMobile()), TerOrder::getMobile, bo.getMobile());
        return lqw;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxSignParam payOrder (TerPayOrderBo bo) {
        WxSignParam wsp = new WxSignParam();
        TerOrderBo tbo = BeanUtil.copyProperties(bo, TerOrderBo.class);
        if(baseMapper.isTimeSlotAvailable(tbo)>0){
            wsp.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            wsp.setMsg("该时间段已被预定,请修改预定时间！");
            return wsp;
        }
        TerOrder order=baseMapper.selectById(bo.getId());
        if(ObjectUtil.isEmpty(order)){
            throw new ServiceException("订单不存在!");
        }
        wsp = commonPay(order);
        if(wsp.getCode()==HttpStatus.HTTP_OK){
            order.setPaymentMethod(wsp.getPayMethod());
        }else {
            return wsp;
        }
        int flag=baseMapper.updateById(order) ;
        if(flag<=0){
            throw new ServiceException("支付失败");
        }
        wsp.setData(order);
        return wsp;
    }
    @Override
    public WxSignParam commonPay(TerOrder order) {
        WxSignParam wsp = new WxSignParam();
        if(order.getPrepayAmount()>0){
            if("ye".equals(order.getPaymentMethod())){
                accountsService.deductBalance(order.getPrepayAmount());
                wsp.setCode(HttpStatus.HTTP_OK);
                wsp.setMsg("成功签约");
                wsp.setPayMethod("钱包");
            }else{
                TerAccountsBo abo = new TerAccountsBo();
                abo.setOpenId(order.getOpenId());
                abo.setBalance(order.getPrepayAmount());
                abo.setOrderId(order.getId());
                wsp= accountsService.payAccount(abo);
                wsp.setPayMethod("微信");
            }
        }else{
            TerCouponVo couponVo=couponService.queryById(order.getCouponId());
            wsp.setCode(HttpStatus.HTTP_OK);
            wsp.setMsg("支付成功");
            wsp.setPayMethod(CouponPlateTypeEnum.getNameByType(couponVo.getPlateType()));
        }
        return wsp;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateOrder(TerOrderBo bo){
        //查询用户信息
        TerOrder terOrder=baseMapper.selectById(bo.getId());
        if(ObjectUtil.isEmpty(terOrder)){
            return 0;
        }
        if(OrderStatusContent.CONFIRMED.equals(terOrder.getOrderStatus())) {
            return 1;
        }
        terOrder.setOrderStatus(OrderStatusContent.CONFIRMED);
        //使用优惠卷
        if(ObjectUtil.isNotEmpty(terOrder.getCouponId())){
            couponService.useCoupon(terOrder.getCouponId(),LoginHelper.getAppLoginUser().getId(), terOrder.getId());
        }
        Integer flag=baseMapper.updateById(terOrder);
        //查询当前用户结束时间早于当前时间但不超过30分钟的订单
        Integer isOpenDoor=baseMapper.selectRecentlyExpiredOrders(30,terOrder.getOpenId());
        //判断是否是续单或者原订单结束后30分钟内续单，自动进行开门上电操作
        if(StrUtil.isNotBlank(terOrder.getOldId()) || isOpenDoor>0) {
            renewOrder(terOrder);
        }
        ThreadUtil.execAsync(()->wxMaNoticeService.placeOrder(terOrder.getId()));
        return flag;
    }


    public void renewOrder(TerOrder order){
        TerUseKeyBo boKey=new TerUseKeyBo();
        boKey.setPresetTime(order.getPresetTime());
        boKey.setId(order.getId());
        boKey.setOpenId(order.getOpenId());
        boKey.setTearoomId(order.getTearoomId());
        boKey.setOrderStatus(order.getOrderStatus());
        boKey.setEndTime(order.getStartTime());
        boKey.setOldId(order.getOldId());
        useKey(boKey);
    }
    /**
     * 新增订单
     *
     * @param bo 订单
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TerOrderVo insertByBo(TerOrderBo bo) {
        //校验茶室是否可用
        TerRoomDetailsVo roomdetail = roomBaseMapper.selectVoById(bo.getTearoomId());
        if(roomdetail!=null&&roomdetail.getStatus().equals(RoomStatusEnum.UPKEEP.getType())) {
            throw new RuntimeException("茶室维护中，请更换茶室...");
        }
        TerUserVo userVo=terUserService.queryById(LoginHelper.getAppLoginUser().getId());
        if(ObjectUtil.isEmpty(userVo)) {
            throw new ServiceException("用户不存在!");
        }
        if(roomdetail.getStartTime()!=null&&!"00:00".equals(roomdetail.getStartTime())&&isNotWithinRange(bo.getStartTime(),bo.getEndTime(),roomdetail.getStartTime(),roomdetail.getEndTime())){
            throw new ServiceException("非营业时间范围,请重新选择!");
        }
        bo.setId(generateOrderId());
        bo.setOrderStatus(OrderStatusContent.UN_PAY);
        bo.setViewStartTime(bo.getStartTime());
        bo.setStartTime(bo.getStartTime().minusMinutes(15));
        bo.setViewEndTime(bo.getEndTime());
        bo.setCreateTime(LocalDateTime.now());

        //新增茶室详情信息
        TerOrder add = BeanUtil.toBean(bo, TerOrder.class);
        Integer payPrice=new BigDecimal(roomdetail.getTeroomHprice()).multiply(new BigDecimal(bo.getPresetTime())).intValue();
        add.setTotalPrice(payPrice);
        add.setPrepayAmount(add.getTotalPrice());
        add.setTenantId("000000");
        add.setMobile(userVo.getMobile());
        if(bo.getOldId()!=null){
            TerOrderBo renebo = new TerOrderBo();
            renebo.setId(bo.getOldId());
            renebo.setIsRenew("1");
            this.updateByBo(renebo);
        }
        baseMapper.insert(add);
        TerOrderVo vo=couponService.calculatePrice(null, LoginHelper.getAppLoginUser().getId(),add.getId());
        return vo;
    }

    /**
     * 修改订单
     *
     * @param bo 订单
     * @return 是否修改成功
     */
    public Boolean updateByBo(TerOrderBo bo) {
        bo.setUpdateTime(LocalDateTime.now());
        TerOrder update = BeanUtil.toBean(bo, TerOrder.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TerOrder entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除订单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }


    @Override
    public UseKeyParam getKey(TerUseKeyBo bo){
        TerOrderBo tbo = new TerOrderBo();
        tbo.setTearoomId(bo.getTearoomId());
        tbo.setOpenId(bo.getOpenId());
        tbo.setId(bo.getId());
        //密钥生成
        UseKeyParam   useKeyParam =new UseKeyParam();
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;;
        LocalDateTime date = LocalDateTime.parse(bo.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        KuodaoParam kuodao;
        try {
            kuodao= keyGenFuc( tbo, date.format(formatter));
        } catch (Exception e) {
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("获取异常，请稍后...");
            return useKeyParam;
        }
        if(kuodao==null||kuodao.getKeyID()==null){
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("获取异常，请稍后...");
            return useKeyParam;
        }
        tbo.setKeyId(kuodao.getKeyID());
        tbo.setOrderStatus(OrderStatusContent.CHECKED_IN);
        tbo.setDoorKey(kuodao.getPinCode());
        tbo.setPresetTime(bo.getPresetTime());
        this.updateByBo(tbo);
        //更新茶室状态
        TerRoomDetailsBo rbo = new TerRoomDetailsBo();
        rbo.setId(bo.getTearoomId());
        rbo.setUpdateBy(1L);
        rbo.setStatus(RoomStatusEnum.USED.getType());
        TerRoomDetails update =BeanUtil.toBean(rbo, TerRoomDetails.class);
        roomBaseMapper.updateById(update);
        //开电
        terIotDeviceService.operateDeviceOnOffByClass(bo.getTearoomId(),true, IotDeviceClassEnum.CHOPPER.getType());
        useKeyParam.setDoorKey(kuodao.getPinCode());
        useKeyParam.setKeyId(kuodao.getKeyID());
        useKeyParam.setId(bo.getId());
        useKeyParam.setMobile(kuodao.getMobile());
        useKeyParam.setCode(HttpStatus.HTTP_OK);
        useKeyParam.setMsg("申请密钥完成");
        return useKeyParam;
    }

    /**
     * 密钥使用
     *
     * @param bo 是否进行有效性校验
     * @return 是否删除成功
     */

    @Override
    public UseKeyParam useKey(TerUseKeyBo bo){
        UseKeyParam useKeyParam =new UseKeyParam();
        TerOrderBo tbo = BeanUtil.copyProperties(bo, TerOrderBo.class);
        //时间计算
        LocalDateTime now;
        if(StrUtil.isNotBlank(tbo.getOldId())){
            now = LocalDateTime.parse(bo.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).plusMinutes(15);
        }else{
            now = LocalDateTime.now();
        }
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;;
        float df = bo.getPresetTime() * 60;
        LocalDateTime afterAdding = now.plusMinutes((int)df);
        String EndDate=afterAdding.format(formatter);
        tbo.setStartTime(now);
        tbo.setEndTime(afterAdding);
        TerRoomDetailsVo roomdetail = roomBaseMapper.selectVoById(bo.getTearoomId());
        if(roomdetail!=null&&roomdetail.getStatus().equals(RoomStatusEnum.UPKEEP.getType())) {
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("茶室紧急维护中，请更换茶室...");
            return useKeyParam;
        }
        if(roomdetail.getStartTime()!=null&&!"00:00".equals(roomdetail.getStartTime())&&isNotWithinRange(now,afterAdding,roomdetail.getStartTime(),roomdetail.getEndTime())){
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("非营业时间范围,请重新选择");
            return useKeyParam;
        }

        if(baseMapper.isTimeSlotAvailable(tbo)>0){
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("目前时间段已被预定,请等待！");
            return useKeyParam;
        }
        KuodaoParam kuodao;
        //密钥生成
        try{
            kuodao= keyGenFuc( tbo, EndDate);
        }catch(Exception e){
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("获取异常，请稍后...");
            return useKeyParam;
        }
        if(kuodao==null||kuodao.getKeyID()==null){
            useKeyParam.setCode(HttpStatus.HTTP_INTERNAL_ERROR);
            useKeyParam.setMsg("获取异常，请稍后...");
            return useKeyParam;
        }
        tbo.setKeyId(kuodao.getKeyID());
        tbo.setOrderStatus(OrderStatusContent.CHECKED_IN);
        tbo.setDoorKey(kuodao.getPinCode());
        tbo.setPresetTime(bo.getPresetTime());
        tbo.setStartTime(now);
        tbo.setEndTime(afterAdding);
        tbo.setViewStartTime(now);
        tbo.setViewEndTime(afterAdding);
        this.updateByBo(tbo);
        //更新茶室状态
        TerRoomDetailsBo rbo = new TerRoomDetailsBo();
        rbo.setId(bo.getTearoomId());
        rbo.setStatus(RoomStatusEnum.USED.getType());
        rbo.setUpdateBy(1L);
        TerRoomDetails update =BeanUtil.toBean(rbo, TerRoomDetails.class);
        roomBaseMapper.updateById(update);
        //开电
        terIotDeviceService.operateDeviceOnOffByClass(bo.getTearoomId(),true, IotDeviceClassEnum.CHOPPER.getType());
        useKeyParam.setDoorKey(kuodao.getPinCode());
        useKeyParam.setKeyId(kuodao.getKeyID());
        useKeyParam.setEndTime(afterAdding);
        useKeyParam.setStartTime(now);
        useKeyParam.setId(bo.getId());
        useKeyParam.setMobile(kuodao.getMobile());
        useKeyParam.setCode(HttpStatus.HTTP_OK);
        useKeyParam.setMsg("申请密钥完成");
        return useKeyParam;
    }
    public static boolean isNotWithinRange(LocalDateTime targetStartTime,LocalDateTime targetEndTime, String startTimeStr, String endTimeStr) {
        // 解析时间字符串为LocalTime
        LocalTime startTime = LocalTime.parse(startTimeStr);
        LocalTime endTime = LocalTime.parse(endTimeStr);

        // 获取目标时间的日期部分
        LocalDate date = targetStartTime.toLocalDate();

        // 构建当天的开始和结束时间
        LocalDateTime startDateTime = date.atTime(startTime);
        LocalDateTime endDateTime = date.atTime(endTime);

        // 检查目标时间是否在范围内（包含边界）
        return targetStartTime.isBefore(startDateTime)||targetEndTime.isAfter(endDateTime);
    }


    /**
     * 密钥申请
     */
    public KuodaoParam keyGenFuc(TerOrderBo bo,String EndDate) throws IOException {
        KuodaoParam kuodaoParam = new KuodaoParam();
        //查询用户信息
        TerUserBo tub = new TerUserBo();
        tub.setOpenId(bo.getOpenId());
        List<TerUserVo> listu = terUserService.queryList(tub);
        //查询配置信息
        TerLockSerialnumBo tlbo = new TerLockSerialnumBo();
        tlbo.setTeroomId(bo.getTearoomId());
        List<TerLockSerialnumVo> list= terLockSerialnumService.queryList(tlbo);
        if(CollUtil.isEmpty(list)){
            throw new ServerException("数据库房间锁查询暂无!");
        }
        kuodaoParam.setRoomID(list.get(0).getHid().toString());
        kuodaoParam.setSysID(list.get(0).getAid().toString());
        kuodaoParam.setOwner(listu.get(0).getNickname().toString());
        kuodaoParam.setUserId(listu.get(0).getMobile());
        kuodaoParam.setMobile(listu.get(0).getMobile());
        kuodaoParam.setEndDate(EndDate);
        KuodaoParam returnkuod= kuodaoService.applyKey(kuodaoParam);

//       if(returnkuod!=null)returnkuod.setMobile(listu.get(0).getMobile());
        //三方密钥申请
        return returnkuod;

    }
    /**
     * 组合主键
     * @return 主键string
     */
    public String generateOrderId() {
        SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
        String date = sdf.format(new Date());
        String sequence = String.format("%06d", new Random().nextInt(999999));
        String suffix = RandomStringUtils.randomAlphanumeric(4); // 4位随机字母数字
        return String.format("TEH-%s%s%s", date, sequence, suffix);

    }

    @Override
    public List<Map<String, Integer>> queryTimelist(TerOrderTimeBo bo){
        //查询已占用时间
        LambdaQueryWrapper<TerOrder> lqw= Wrappers.lambdaQuery();
        lqw.orderByAsc(TerOrder::getStartTime);
        List<String> list =new ArrayList<>();
        list.add(OrderStatusContent.CONFIRMED);
        list.add(OrderStatusContent.CHECKED_IN);
        lqw.in(TerOrder::getOrderStatus,list);
        lqw.eq(TerOrder::getTearoomId,bo.getTearoomId());
        List<TerOrder> order = baseMapper.selectList(lqw);

        if(bo.getStartDate()==null||bo.getStartDate()==LocalDate.now()){
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime targetDateTime = now
                    .plusDays(6)          // 6天的同一时间
                    .withHour(23)         // 设置小时为 23
                    .withMinute(30)       // 设置分钟为 30
                    .withSecond(0)        // 可选：设置秒为 0（默认可能不为 0）
                    .withNano(0);
            return TimeShiftUtil.findFreeTimeSlotsByDay(order,now,targetDateTime);
        }else{
            LocalDateTime start =bo.getStartDate().atTime(0, 0, 0);
            LocalDateTime targetDateTime = start
                    .plusDays(6)          // 6天的同一时间
                    .withHour(23)         // 设置小时为 23
                    .withMinute(30)       // 设置分钟为 30
                    .withSecond(0)        // 可选：设置秒为 0（默认可能不为 0）
                    .withNano(0);
            return TimeShiftUtil.findFreeTimeSlotsByDay(order,start,targetDateTime);

        }
    }
    @Override
    public int  updateStatus(TerOrderBo bo){
        bo.setOrderStatus(OrderStatusContent.CHECKED_IN);
        TerOrder update = BeanUtil.toBean(bo, TerOrder.class);
        terIotDeviceService.operateDeviceOnOffByClass(bo.getTearoomId(),true, IotDeviceClassEnum.CHOPPER.getType());
        return baseMapper.updateById(update);
    }
    private static final Set<String> PAYMENT_METHODS = Set.of("自营", "美团", "快手", "抖音");

    @Override
    public Boolean orderRefund(TerOrderBo bo){
        TerOrder terOrder=baseMapper.selectById(bo.getId());
        if(ObjectUtil.isEmpty(terOrder)){
            throw new ServiceException("订单不存在,无法退款!");
        }
        String refundId =getRefundId(bo);
        terOrder.setOrderStatus(OrderStatusContent.RETURNED);
        terOrder.setRefundId(refundId);
        terOrder.setRevenue(0);
        baseMapper.updateById(terOrder);
        if(StrUtil.isNotBlank(terOrder.getDoorKey())){
            TerRoomDetails terRoomDetails=roomBaseMapper.selectById(terOrder.getTearoomId());
            terRoomDetails.setStatus(RoomStatusEnum.NEED_CLEAN.getType());
            roomBaseMapper.updateById(terRoomDetails);
        }
        return true;
    }

    @Override
    public String getRefundId(TerOrderBo bo){
        TerUserBo tub = new TerUserBo();
        tub.setOpenId(bo.getOpenId());
        List<TerUserVo> listu = terUserService.queryList(tub);
        if(listu==null&&listu.size()<0) {
            throw new ServiceException("用户不存在!");
        }
        if(bo.getKeyId()!=null){
            kuodaoService.revoke(bo.getKeyId());
        }
        if(PAYMENT_METHODS.contains(bo.getPaymentMethod())){
            return "";
        }else if("钱包".equals(bo.getPaymentMethod())){
           return accountsService.balanceRefund(String.valueOf(listu.get(0).getId()));
        }else{
            WxPayVo wdto = new WxPayVo();
            wdto.setOutTradeNo(bo.getRefundId()!=null?bo.getRefundId():bo.getId());
            wdto.setOutRefundNo(generateOrderId());
            wdto.setNotifyUrl(wxPayProperties.getNotifyUrl());
            wdto.setTotalFee(bo.getPrepayAmount());
            payService.wxRefund(wdto);
            return wdto.getOutRefundNo();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer replaceOrder(TerOrderBo bo){
        if (OrderStatusContent.UN_PAY.equals(bo.getOrderStatus())
            ||OrderStatusContent.COMPLETED.equals(bo.getOrderStatus())
            ||OrderStatusContent.RETURNED.equals(bo.getOrderStatus())) {
            throw new ServiceException("该订单状态不可更换");
        }
        if (bo.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException("结束时间不能早于当前时间");
        }
        if (bo.getStartTime().isAfter(bo.getEndTime())) {
            throw new ServiceException("开始时间不能晚于结束时间");
        }
        if (bo.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException("结束时间不能早于当前时间");
        }

        TerOrderBo  update=new TerOrderBo();
        update.setId(bo.getId());
        update.setOrderStatus(OrderStatusContent.REPLACED);
        if(!updateByBo(update))return 0;
        //校验茶室是否可用
        TerRoomDetailsVo roomdetail = roomBaseMapper.selectVoById(bo.getTearoomId());
        if(roomdetail!=null&&roomdetail.getStatus().equals(RoomStatusEnum.UPKEEP.getType())) {
            throw new RuntimeException("茶室维护中，请更换茶室...");
        }
        if (roomdetail.getStartTime() != null && !"00:00".equals(roomdetail.getStartTime()) && isNotWithinRange(bo.getStartTime(), bo.getEndTime(), roomdetail.getStartTime(), roomdetail.getEndTime())) {
            throw new ServiceException("非营业时间范围,请重新选择!");
        }
        //新增茶室详情信息
        TerOrder add = baseMapper.selectById(bo.getId());
        if(add==null) {
            throw new ServiceException("原订单不存在!");
        }
        if(calculateHalfHours(bo.getStartTime().plusMinutes(15),bo.getEndTime())>add.getPresetTime()){
            throw new ServiceException("调整时间大于原订单预定时间!");
        }
        if (add.getTearoomId()==bo.getTearoomId()&&OrderStatusContent.CHECKED_IN.equals(add.getOrderStatus())) {
            throw new ServiceException("已入住的未更换舱体，禁止调整时间！");
        }
        bo.setOpenId(add.getOpenId());
        if(baseMapper.isTimeSlotAvailable(bo)>0){
            throw new ServiceException("目标舱体对应时间段已被预定！");
        }
        if(add.getRefundId()==null){
            add.setRefundId(bo.getId());
        }
        add.setId(generateOrderId());
        add.setOrderStatus(OrderStatusContent.CONFIRMED);
        add.setTearoomName(bo.getTearoomName());
        add.setTearoomId(bo.getTearoomId());
        add.setCreateTime(LocalDateTime.now());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        add.setStartTime(bo.getStartTime().format(formatter));
        add.setEndTime(bo.getEndTime().format(formatter));
        add.setViewStartTime(bo.getStartTime());
        add.setViewEndTime(bo.getEndTime());
        if(add.getKeyId()!=null){
            kuodaoService.revoke(add.getKeyId());
        }
        add.setKeyId(null);
        add.setDoorKey(null);
        if(baseMapper.insert(add)<=0)return 0;
        //续订时更新原订单
        if(add.getViewStartTime().isBefore(add.getCreateTime())) {
            renewOrder(add);
            ThreadUtil.execAsync(()->wxMaNoticeService.placeOrder(add.getId()));
            return 1;
        }
        ThreadUtil.execAsync(()->wxMaNoticeService.placeOrder(add.getId()));
        return 1;
    }
    public static float calculateHalfHours(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null) {
            throw new IllegalArgumentException("时间参数不能为null");
        }
        // 截断到分钟（忽略秒和纳秒）
        LocalDateTime truncatedStart = start.truncatedTo(ChronoUnit.MINUTES);
        LocalDateTime truncatedEnd = end.truncatedTo(ChronoUnit.MINUTES);

        // 计算分钟差并取绝对值
        long minutes = Duration.between(truncatedStart, truncatedEnd).toMinutes();
        long absMinutes = Math.abs(minutes);

        double hours = absMinutes / 60.0;
        return (float) (Math.round(hours * 2) / 2.0);
    }
}
